package com.rd.sys.service.analysis.scheme.impl;

import java.io.File;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import baseproj.common.exception.BaseServiceException;

import com.rd.sys.common.base.CommonConstants;
import com.rd.sys.common.utils.JacksonMapper;
import com.rd.sys.common.utils.cache.AnalysisCache;
import com.rd.sys.common.utils.client.AnalysisPathUtils;
import com.rd.sys.common.utils.file.FileLockUtils;
import com.rd.sys.common.utils.file.HandlerCaller;
import com.rd.sys.common.utils.image.ImageUtils;
import com.rd.sys.dto.client.analysis.AoiData;
import com.rd.sys.dto.client.store.Material;
import com.rd.sys.dto.client.store.Question;
import com.rd.sys.dto.client.store.Scheme;
import com.rd.sys.dto.commmon.enums.SyncEnum;
import com.rd.sys.service.analysis.scheme.SchemeService;

@Service
@Transactional(rollbackFor = {Exception.class})
public class SchemeServiceImpl implements SchemeService {
    protected static final Logger logger = LoggerFactory.getLogger(SchemeServiceImpl.class);

    /**
     * 查询方案描述详情
     * 
     * @param userCode
     * @param projectCode
     * @return
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<Scheme> querySchemeList(String userCode, String projectCode) {
        try {
            /** 从缓存中读取 ,没有就从文件中读取 */
            String key = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode;
            List<Scheme> schemeList = AnalysisCache.SchemeCache.get(key);

            return schemeList;

        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception ex) {
            logger.error("query scheme list error", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 查询某个指定的方案
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @return
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public Scheme queryScheme(String userCode, String projectCode, String schemeCode) {
        Scheme scheme = null;
        try {
            List<Scheme> schemeList = querySchemeList(userCode, projectCode);

            if (schemeList != null && schemeList.size() > 0) {
                Iterator<Scheme> iterator = schemeList.iterator();
                while (iterator.hasNext()) {
                    Scheme dto = iterator.next();
                    if (dto.getSchemeCode().equals(schemeCode)) {
                        scheme = dto;
                        break;
                    }
                }
            }

            return scheme;

        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception ex) {
            logger.error("query scheme error", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 查询某个方案的所有素材详情
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @return
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<Material> queryMaterialList(String userCode, String projectCode, String schemeCode) {
        try {
            List<Material> materialList = null;

            /** 从缓存中读取所有方案信息 */
            String key = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode;
            List<Scheme> schemeList = AnalysisCache.SchemeCache.get(key);

            /** 得到需查询的查询的方案 */
            Iterator<Scheme> iterator = schemeList.iterator();
            while (iterator.hasNext()) {
                Scheme scheme = iterator.next();
                if (scheme.getSchemeCode().equals(schemeCode)) {
                    materialList = scheme.getMaterials();
                    break;
                }
            }

            return materialList;

        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception ex) {
            logger.error("query material list error", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 查询某个方案的指定素材
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param materialCode
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public Material queryMaterial(String userCode, String projectCode, String schemeCode, String materialCode) {
        Material material = null;
        try {
            List<Material> materialList = queryMaterialList(userCode, projectCode, schemeCode);

            if (materialList != null && materialList.size() > 0) {
                Iterator<Material> iterator = materialList.iterator();
                while (iterator.hasNext()) {
                    Material dto = iterator.next();
                    if (dto.getCode().equals(materialCode)) {
                        material = dto;
                        break;
                    }
                }
            }

            return material;

        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception ex) {
            logger.error("query material error", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 展示素材图片
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param materialName
     * @param response
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void showMaterialImage(String userCode, String projectCode, String schemeCode, String materialCode,
            String materialSuffix, HttpServletResponse response) {
        try {
            String fileName = AnalysisPathUtils.findMaterialFile(userCode, projectCode, schemeCode, materialCode,
                    materialSuffix);

            // 设置浏览器缓存时间
            response.setHeader("Cache-Control", "public,max-age="
                    + CommonConstants.AnalysisConstats.MATERIAL_IMAGE_CACHE_TIME);

            ImageUtils.show(fileName, response);
        } catch (Exception ex) {
            logger.error("show material image error", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 从方案描述文件中读取方案信息
     * 
     * @param key
     * @return
     * @throws Exception
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<Scheme> readSchemeList(String key) throws Exception {
        List<Scheme> schemeList = null;

        /** key由usercode和projectcode组成 */
        String[] keys = key.split(CommonConstants.AnalysisConstats.CACHE_SPLIT);
        String userCode = keys[0];
        String projectCode = keys[1];

        /** 从方案描述文件中读取出来 */
        File file = new File(AnalysisPathUtils.findSchemeFileName(userCode, projectCode));
        if (!file.exists()) {
            file.createNewFile(); // 对文件为空时做个保护
            schemeList = new LinkedList<Scheme>();
        } else {
            byte[] schemeBytes = FileUtils.readFileToByteArray(file);
            if (schemeBytes.length > 0) {
                String schemeJson = new String(schemeBytes, CommonConstants.SysParamConstants.SYS_CHARSET);
                schemeList = (List<Scheme>) JacksonMapper.json2List(schemeJson, Scheme.class);
            } else {
                schemeList = new LinkedList<Scheme>();
            }
        }
        return schemeList;
    }

    /**
     * 同步方案信息
     * 
     * @param userno
     * @param userCode
     * @param projectCode
     * @param scheme
     *            待新增的方案
     * @param opercode
     *            操作代码
     * @throws Exception
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void syncScheme(Integer userno, String userCode, String projectCode, Scheme scheme, String opercode)
            throws Exception {
        try {
            /** 执行实际同步信息 */
            File lockFile = new File(AnalysisPathUtils.findSchemeFileName(userCode, projectCode)
                    + CommonConstants.AnalysisConstats.SYNC_FILE_LOCK_SUFFIX);
            File srcFile = new File(AnalysisPathUtils.findSchemeFileName(userCode, projectCode));
            File schemePath = new File(AnalysisPathUtils.findSchemePath(userCode, projectCode, scheme.getSchemeCode()));

            // 定义回调执行流程
            String key = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode;
            Object[] array = new Object[] {key, scheme, srcFile, schemePath, opercode};
            HandlerCaller caller = new HandlerCaller(array) {
                @Override
                public void execute(FileChannel lockChannel) throws Exception {
                    /** 1.分解各个字段 */
                    Object[] array = (Object[]) this.object;
                    String key = (String) array[0];
                    Scheme scheme = (Scheme) array[1];
                    File srcFile = (File) array[2];
                    File schemePath = (File) array[3];
                    String opercode = (String) array[4];

                    /** 2.读取当前方案信息,根据方案编码判断是否存在 */
                    List<Scheme> schemeList = AnalysisCache.SchemeCache.get(key);
                    Iterator<Scheme> iterator = schemeList.iterator();
                    Scheme dto = null;
                    boolean isHave = false;
                    while (iterator.hasNext()) {
                        dto = iterator.next();
                        if (dto.getSchemeCode().equals(scheme.getSchemeCode())) {
                            isHave = true;
                            break;
                        }
                    }

                    if (SyncEnum.Opercode.DELETE.getCode().equals(opercode)) {
                        if (isHave) { // 做个保护,存在则删除,不存在也正常
                            schemeList.remove(dto);
                        }
                    } else if (SyncEnum.Opercode.ADD.getCode().equals(opercode)
                            || SyncEnum.Opercode.EDIT.getCode().equals(opercode)) {
                        if (isHave) {
                            dto.setSchemeDesc(scheme.getSchemeDesc()); // 只更新方案描述
                        } else {
                            schemeList.add(scheme);
                        }
                    }

                    /** 3.同步的方案信息转换 */
                    String newSchemeJson = JacksonMapper.list2Json(schemeList);
                    ByteBuffer buffer = ByteBuffer.wrap(newSchemeJson
                            .getBytes(CommonConstants.SysParamConstants.SYS_CHARSET));

                    /** 4.写入并更新缓存 */
                    FileChannel srcChannel = null;
                    try {
                        srcChannel = new FileOutputStream(srcFile).getChannel();
                        srcChannel.write(buffer); // 实际写入描述文件

                        if (!schemePath.exists()) { // 创建方案目录
                            schemePath.mkdir();
                        }

                        // 处理方案目录
                        if (SyncEnum.Opercode.DELETE.getCode().equals(opercode)) {
                            FileUtils.deleteDirectory(schemePath); // 删除方案目录
                        } else if (SyncEnum.Opercode.ADD.getCode().equals(opercode)) {
                            if (!schemePath.exists()) { // 创建方案目录
                                schemePath.mkdir();
                            }
                        }

                        AnalysisCache.SchemeCache.put(key, schemeList); // 更新缓存
                    } catch (Exception ex) {
                        throw ex;
                    } finally {
                        if (srcChannel != null) {
                            try {
                                srcChannel.close();
                            } catch (Exception ex) {
                                logger.error("", ex);
                            }
                        }
                    }
                }
            };

            FileLockUtils.handlerByLockFile(lockFile, caller); // 调用文件锁定方式操作
        } catch (Exception ex) {
            logger.error("", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 查询某个被试属性详情
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param attributeNo
     *            属性No
     * @return
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public Question queryResultAttribute(String userCode, String projectCode, String schemeCode, Integer attributeNo)
            throws Exception {
        Scheme scheme = this.queryScheme(userCode, projectCode, schemeCode);
        if (scheme.getQuestions() != null && scheme.getQuestions().size() > 0) {
            Iterator<Question> iterator = scheme.getQuestions().iterator();
            Question question = null;
            while (iterator.hasNext()) {
                question = iterator.next();
                if (question.getType() == Question.TYPE.Attribute.getCode()) {
                    if (question.getNo() == attributeNo) {
                        return question;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 从AOI数据文件中读取AOI的图形信息
     * 
     * @param key
     * @return
     * @throws Exception
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<AoiData> readAoiDataList(String key) throws Exception {
        List<AoiData> aoiGraphsList = null;

        /** key由usercode和projectcode组成 */
        String[] keys = key.split(CommonConstants.AnalysisConstats.CACHE_SPLIT);
        String userCode = keys[0];
        String projectCode = keys[1];
        String schemeCode = keys[2];

        /** 从AOI数据文件中读取出来 */
        File file = new File(AnalysisPathUtils.findAoiDataFileName(userCode, projectCode, schemeCode));
        if (!file.exists()) {
            file.createNewFile(); // 对文件为空时做个保护
            aoiGraphsList = new LinkedList<AoiData>();
        } else {
            byte[] aoiBytes = FileUtils.readFileToByteArray(file);
            if (aoiBytes.length > 0) {
                String aoiJson = new String(aoiBytes, CommonConstants.SysParamConstants.SYS_CHARSET);
                aoiGraphsList = (List<AoiData>) JacksonMapper.json2List(aoiJson, AoiData.class);
            } else {
                aoiGraphsList = new LinkedList<AoiData>();
            }
        }
        return aoiGraphsList;
    }

    /**
     * 同步AOI信息
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param aoiGraphsList
     *            待更新的AOI信息
     * @throws Exception
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void syncAoiData(String userCode, String projectCode, String schemeCode, List<AoiData> aoiGraphsList)
            throws Exception {
        try {
            /** 执行实际同步信息 */
            File lockFile = new File(AnalysisPathUtils.findAoiDataFileName(userCode, projectCode, schemeCode)
                    + CommonConstants.AnalysisConstats.SYNC_FILE_LOCK_SUFFIX);
            File srcFile = new File(AnalysisPathUtils.findAoiDataFileName(userCode, projectCode, schemeCode));

            // 定义回调执行流程
            String key = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT + schemeCode;
            Object[] array = new Object[] {key, aoiGraphsList, srcFile};
            HandlerCaller caller = new HandlerCaller(array) {
                @Override
                public void execute(FileChannel lockChannel) throws Exception {
                    /** 1.分解各个字段 */
                    Object[] array = (Object[]) this.object;
                    String key = (String) array[0];
                    List<AoiData> aoiGraphsList = (List<AoiData>) array[1];
                    File srcFile = (File) array[2];

                    /** 2.同步的AOI信息转换 */
                    String aoiGraphs = JacksonMapper.list2Json(aoiGraphsList);
                    ByteBuffer buffer = ByteBuffer.wrap(aoiGraphs
                            .getBytes(CommonConstants.SysParamConstants.SYS_CHARSET));

                    /** 3.写入并更新缓存 */
                    FileChannel srcChannel = null;
                    try {
                        srcChannel = new FileOutputStream(srcFile).getChannel();
                        srcChannel.write(buffer); // 实际写入描述文件

                        AnalysisCache.AoiDataCache.put(key, aoiGraphsList); // 更新缓存
                    } catch (Exception ex) {
                        throw ex;
                    } finally {
                        if (srcChannel != null) {
                            try {
                                srcChannel.close();
                            } catch (Exception ex) {
                                logger.error("", ex);
                            }
                        }
                    }
                }
            };

            FileLockUtils.handlerByLockFile(lockFile, caller); // 调用文件锁定方式操作
        } catch (Exception ex) {
            logger.error("", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 查询某个方案中的所有AOI信息
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @return
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<AoiData> queryAoiData(String userCode, String projectCode, String schemeCode) {
        try {
            /** 从缓存中读取 ,没有就从文件中读取 */
            String key = userCode + CommonConstants.AnalysisConstats.CACHE_SPLIT + projectCode
                    + CommonConstants.AnalysisConstats.CACHE_SPLIT + schemeCode;
            List<AoiData> aoiGraphsList = AnalysisCache.AoiDataCache.get(key);

            return aoiGraphsList;

        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception ex) {
            logger.error("query aoi of scheme error", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 查询某个方案中的某个指定AOI信息
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @return
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public AoiData queryAoiData(String userCode, String projectCode, String schemeCode, int aoiIndex) {
        try {
            List<AoiData> aoiGraphsList = queryAoiData(userCode, projectCode, schemeCode);

            AoiData aoiGraph = null;
            logger.info("query single aoi of scheme by aoiIndex :" + aoiIndex);
            if (aoiGraphsList != null && aoiGraphsList.size() > aoiIndex) { // aoiIndexb必然会小于保存的aoiGraphsList
                aoiGraph = aoiGraphsList.get(aoiIndex);
            }

            return aoiGraph;

        } catch (BaseServiceException be) {
            throw be;
        } catch (Exception ex) {
            logger.error("query single aoi of scheme error", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 同步AOI信息
     * 
     * @param userCode
     * @param projectCode
     * @param schemeCode
     * @param aoiGraphsList
     *            待更新的AOI信息
     * @throws Exception
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void saveAoiData(String userCode, String projectCode, String schemeCode, AoiData aoiGraph) {
        try {
            List<AoiData> aoiGraphsList = queryAoiData(userCode, projectCode, schemeCode);
            if (aoiGraphsList != null && aoiGraphsList.size() > 0) {
                Iterator<AoiData> iterator = aoiGraphsList.iterator();
                while (iterator.hasNext()) {
                    AoiData dto = iterator.next();
                    if (dto.getName().equals(aoiGraph.getName())) { // 修改相关值
                        dto.setFillStyle(aoiGraph.getFillStyle());
                        dto.setStrokeStyle(aoiGraph.getStrokeStyle());
                        dto.setShowName(aoiGraph.getShowName());
                        break;
                    }
                }
                syncAoiData(userCode, projectCode, schemeCode, aoiGraphsList); // 更新AOI数据
            }
        } catch (Exception ex) {
            logger.error("save single aoi of scheme error", ex);
            throw new RuntimeException(ex);
        }
    }
}
